3/8/2018

R WebMaps with Leaflet

Welcome! While we're waiting:

Introduction

Outline

Basic Maps

  • Marker Maps
  • Adding Popups

Data Maps

  • Symbology for mapping point data
  • Choropleth maps
  • Adding Legends

Doing More

  • Customizing the UI
  • Sharing your maps

Setup

This workshop/tutorial will walk you through the basics of using the Leaflet mapping package in R. You can follow along in any of the three formats:

  • Tutorial Page (leaflet-webmaps-in-R-sp2018.html)
  • Raw code (leaflet-webmaps-in-R-sp2018.R)
  • Slides (leaflet-webmaps-in-R-sp2018-slides.html)

To begin, lets set up our packages and environment.

Loading packages

Load the packages we will use today

library(leaflet)
library(RColorBrewer)
library(sp)
library(rgdal)
library(htmlwidgets)
library(magrittr) # or dplyr

Install any packages that you do not have on your computer

# install.packages("leaflet")
# install.packages("RColorBrewer")
# install.packages("sp")
# install.packages("rgdal")
# install.packages("htmlwidgets")
# install.packages("magrittr") # or dplyr

Set working directory

Leaflet

Leaflet

Leaflet is a lightweight, yet powerful javascript library for creating interactive web maps.

Leaflet maps are a combination of HTML and Javascript code that is meant to be rendered in a web browser.

We can use the R leaflet package to create Leaflet maps in R

Our First Leaflet Map

Our first Leaflet map

map1 <- leaflet()       # Initialize the map object
map1 <- addTiles(map1)  # Add basemap tiles
map1                    # Display the map

Our first Leaflet map

Setting the view

Note, we are adding to an exisiting map1

map1 <- setView(map1, lat=37.870044, lng=-122.258169, zoom = 15)
map1

Setting the view

Piping Syntax

Requires dplyr or magrittr package to be loaded

map2 <- leaflet() %>%
        addTiles() %>%  
        setView(lat=37.870044, lng=-122.258169, zoom = 15)
map2   

Piping gives us the same map

map2

Syntax Comparison

Regular

map1 <- leaflet()        
map1 <- addTiles(map1)   
map1 <- setView(map1, lat=37.870044, lng=-122.258169, zoom = 15)
map1                  

Piping

map2 <- leaflet() %>%
        addTiles() %>%  
        setView(lat=37.870044, lng=-122.258169, zoom = 18)
map2   

WARNING: Don't name your map object map

Challenge

Rerun the piping code changing the zoom level

  • What zoom level reveals the campus building that the map is centered on?
  • What zoom level shows all of berkeley?
  • What is the max/min zoom level that returns a basemap?

Changing the basemap

Now try a few

Here's the syntax - instead of using addTiles you use addProviderTiles

map2 <- leaflet() %>%
        addProviderTiles("Esri.WorldStreetMap") %>% 
        setView(lat=37.870044, lng=-122.258169, zoom = 12)

View it

map2

Add Your own map

Source: https://mapwarper.net/maps/25477#Preview_tab

mapurl <- "https://mapwarper.net/maps/tile/25477/{z}/{x}/{y}.png"

map2 <- leaflet() %>%
  addProviderTiles("CartoDB.Positron") %>%
  addTiles(mapurl) %>%  # custom
  setView(lat=37.870044, lng=-122.258169, zoom = 13)

Here we are combining addTiles and addProviderTiles

Add a non-provider map

map2  

Questions?

Mapping Data

Add Data with a Marker

Note that the setView function is commented out.

  • The map will center on the center of the data points and determine an appropriate zoom leve.
  • You can override this by using setView.
map3 <- leaflet() %>%
  addTiles() %>%  # Add default OpenStreetMap map tiles
  #setView(lat=37.870044, lng=-122.258169, zoom = 17) %>%
  addMarkers(lat=37.870044, lng=-122.258169, popup="Go Bears!")

Add Data with a Marker

map3  # Display the map  - Click on the marker

Mapping Data Sets

The Sample Data

San Francisco Open Data Portal

SF Property Tax Rolls

This data set includes the Office of the Assessor-Recorder’s secured property tax roll spanning from 2015.

We are using this as a proxy for home values.

We are working with a simplified sample of the full data set.

Load the CSV file into a data frame

Set your working directory first to the folder where you downloaded the workshop files!

sfhomes <- read.csv('data/sfhomes15.csv', stringsAsFactors = FALSE)  
str(sfhomes)
## 'data.frame':    680 obs. of  11 variables:
##  $ SalesDate   : chr  "2015-08-21" "2015-08-13" "2015-12-29" "2015-07-06" ...
##  $ Address     : chr  "0000 2760 19TH                AV0015" "0000 0560AMISSOURI            ST0000" "0000 0718 LONG BRIDGE         ST1202" "0000 0899 VALENCIA            ST0202" ...
##  $ YrBuilt     : int  1979 2003 2016 2015 1961 1900 2015 NA 1947 1907 ...
##  $ NumBeds     : int  2 2 2 3 3 2 2 0 0 3 ...
##  $ NumBaths    : int  2 2 2 3 3 2 2 0 0 3 ...
##  $ NumUnits    : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ AreaSqFt    : int  1595 1191 1346 1266 1840 1256 1520 536 950 1837 ...
##  $ Neighborhood: chr  "Twin Peaks" "Potrero Hill" "Mission Bay" "Mission" ...
##  $ Value       : int  865000 1402560 2260993 1700000 2309692 2700564 1925000 583768 944180 1750001 ...
##  $ lat         : num  37.7 37.8 37.8 37.8 37.8 ...
##  $ lon         : num  -122 -122 -122 -122 -122 ...

Explore the data

head(sfhomes)
##    SalesDate                              Address YrBuilt NumBeds NumBaths
## 1 2015-08-21 0000 2760 19TH                AV0015    1979       2        2
## 2 2015-08-13 0000 0560AMISSOURI            ST0000    2003       2        2
## 3 2015-12-29 0000 0718 LONG BRIDGE         ST1202    2016       2        2
## 4 2015-07-06 0000 0899 VALENCIA            ST0202    2015       3        3
## 5 2015-06-12 0000 1333 JONES               ST0808    1961       3        3
## 6 2015-04-14 0000 1904 BAKER               ST0000    1900       2        2
##   NumUnits AreaSqFt    Neighborhood   Value      lat       lon
## 1        1     1595      Twin Peaks  865000 37.73601 -122.4741
## 2        1     1191    Potrero Hill 1402560 37.75920 -122.3965
## 3        1     1346     Mission Bay 2260993 37.77181 -122.3942
## 4        1     1266         Mission 1700000 37.75876 -122.4210
## 5        1     1840        Nob Hill 2309692 37.79362 -122.4149
## 6        1     1256 Pacific Heights 2700564 37.78881 -122.4437

Map the data

map4 <- leaflet() %>%
  addTiles() %>%   
  addMarkers(lat=sfhomes$lat, lng=sfhomes$lon, 
            popup= paste("Address:", sfhomes$Address,
                         "<br>", 
                         "Property Value: ", sfhomes$Value))

Map the data

map4    # Thoughts?

Popups Made Easier

We can save the popup code as a string and re-use it instead of typing it over and over again.

  • Note a little HTML knowledge can go a long way!
popup_content <- paste("<b>Address:</b>", sfhomes$Address,"<br>", 
                       "<b>Property Value</b>: ", sfhomes$Value, "<br>",
                       "<b>Neighborhood:</b> ", sfhomes$Neighborhood, "<br>",
                       "<b>Num Bedrooms: </b>", sfhomes$NumBeds, "<br>",
                       "<b>Num Bathrooms:</b>", sfhomes$NumBaths
                       )

Map the data

We can also use shorter syntax

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addMarkers(~lon, ~lat, popup = popup_content)
  • By passing in the name of the data object to leaflet we can reference column values directly.
  • Note also that we are using the tilde operator to map the data to the addMarkers function.
  • When lng and lat are not named (e.g. lng=lon) then they must be in the expected order (lng, lat).

Map the data

map4   # - check out the popups now

Dig deeper

The map is too crowded with Markers.

Read the addMarker documentation for options to address this.

addMarkers(map, lng = NULL, lat = NULL, layerId = NULL, 
           group = NULL, icon = NULL, popup = NULL, 
           options = markerOptions(), 
           clusterOptions = NULL, clusterId = NULL, 
           data = getMapData(map))

Cluster Option

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addMarkers(~lon, ~lat, popup= popup_content,
            clusterOptions = 1)

Cluster Option

map4  # Explore the Map - hover over a cluster marker, zoom in.

Mapping Points as Circles

addCirleMarker

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addCircleMarkers(~lon, ~lat, popup = popup_content)

Mapping Points as Circles

addCirleMarker

map4 

addCircleMarkers

addCircleMarkers(map, lng = NULL, lat = NULL, radius = 10, 
    layerId = NULL, group = NULL, stroke = TRUE, color = "#03F", 
    weight = 5, opacity = 0.5, 
    fill = TRUE, fillColor = color, ....)

Customize the circleMarkers

Change color, radius and stroke weight of circle markers

map4 <- leaflet(sfhomes) %>%
  addTiles() %>%   
  addCircleMarkers(~lon, ~lat, popup = popup_content,
             color="white", radius=6, weight=2,   # stroke
             fillColor="red",fillOpacity = 0.75   # fill
             )

Customize the circleMarkers

map4 

Data Driven Symbology

Cartography

Art + Science

Finding the right symbology - size, color, shape, etc

and mapping it to your data

Often requires a classification scheme

Mapping Points by Size

We can symbolize the size of points by data values by making the radius of the circle a function of a data value.

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= NA, color="Red", weight=1, fillOpacity = 0,
             radius= ~NumBeds+2
             )

Mapping Points by Size

map4

addCircles vs addCircleMarkers

Circles and Circle Markers look quite similar.

Circle radii are specified in meters while Circle Markers are specified in pixels.

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircles(~lon, ~lat, popup=popup_content,
             fillColor= NA, color="Red", 
             weight=1, fillOpacity = 0,
             radius= ~NumBeds*10
             )

map4

Mapping Data by Color

RColorBrewer

The RColorBrewer package is widely used to create color palettes for maps.

  • A color palette is a set of colors

Qualitative Palettes

Contrasting colors for categorical data

display.brewer.all(type="qual") 

display.brewer.pal(7, "Set3" )  # Try a different number of colors

Sequential Palettes

For highlighting trends in numerical data

display.brewer.all(type="seq")

Diverging Palettes

For highlighting the outliers

display.brewer.all(type="div")

Color Mapping in leaflet

colorFactor maps categorical data to colors

colorFactor(palette, domain, levels = NULL, ordered = FALSE,
  na.color = "#808080", alpha = FALSE, reverse = FALSE)

colorNumeric applies a simple linear mapping of numeric data to color palette

colorNumeric(palette, domain, na.color = "#808080", alpha = FALSE,
  reverse = FALSE)

Color Mapping in leaflet

colorQuantile assigns colors to numeric data binned by quantile

colorQuantile(palette, domain, n = 4,
  probs = seq(0, 1, length.out = n + 1),
  na.color = "#808080", alpha = FALSE, reverse = FALSE)

colorBin also assigns colors to binned numeric data, but allows for customization

colorBin(palette, domain, bins = 7, pretty = TRUE, 
         na.color = "#808080", alpha = FALSE, reverse = FALSE)

Mapping Categories

Map Homes by Neighborhood

  • with the Leaflet colorFactor function
  • Check out the available RColorBrewer palettes first
display.brewer.all(type="qual")

colorFactor

colorfactor takes as input a color palette and a data object (domain) that contains the full range of possible values to be mapped.

colorfactor returns a function specific to that domain that can be used to output a range of color values.

# Create a qualitative color palette
myColors <- colorFactor("Paired", sfhomes$Neighborhood) 

Test it

the_color_values <- myColors(sfhomes$Neighborhood)
length(the_color_values)
## [1] 680
length(the_color_values) == length(sfhomes$Neighborhood)
## [1] TRUE
unique(the_color_values)
##  [1] "#B15928" "#C4AAD2" "#E83F26" "#F57166" "#FBAE63" "#F6905B" "#3EA433"
##  [8] "#BFA19E" "#EDD279" "#4F8EC1" "#9BD277" "#FF9939" "#A6CEE3" "#7E54A6"
## [15] "#79A6A3" "#C3A176"

Homes by Neighborhood

Using a color palette

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat,  
             popup= popup_content,
             fillColor= ~myColors(Neighborhood),
             radius=6, color=NA, weight=2, fillOpacity = 1
             )

Homes by Neighborhood

map4  # what neighborhood had the most sales?

Add a Legend

Add a Legend

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~myColors(Neighborhood),
             radius=6, color=NA, weight=2,fillOpacity = 1
             ) %>%
      addLegend(title = "Neighborhood", pal =  myColors,
                values = ~Neighborhood, opacity = 1, 
                position="bottomleft")

Add a legend

map4 

Mapping Colors to Numeric Values

For simple linear scaling of colors to values use colorNumeric

#display.brewer.all(type="seq")
numColors <- colorNumeric("Reds", sfhomes$Value)

Proportional Color Map

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~numColors(Value),
             radius=6, color="grey", weight=1, fillOpacity = 1
             ) %>%
      addLegend(title = "Property Values", pal =  numColors,
                values = ~Value, opacity = 1, 
                position="bottomleft")

map4

Quantile Colors

You can use colorQuantile to create a color palette based on quantiles

?colorQuantile

Default is 4 bins, but you can set n manually (3 to 7)

#display.brewer.all(type="div")
quantColors <- colorQuantile("Reds", sfhomes$Value, n=5)

map4 <- leaflet(sfhomes) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~quantColors(Value),
             radius=6, color="grey", weight=1,fillOpacity = 1
             ) 

map4

Add A Legend

map4 %>%  addLegend(title = "Value", pal =  quantColors,
                values = ~Value, opacity = 1, 
                position="bottomleft")

Customize the Legend

map5 <-map4 %>%   addLegend(pal = quantColors, values = ~Value,
                     title = "Property Value, 2015",
                     position="bottomleft",
                     opacity=1,
                     labFormat = function(type, cuts, p) {
                      n = length(cuts)
                      cuts = paste0("$", format(cuts[-n], big.mark=","), 
                              " - ", "$",format(cuts[-1], big.mark=","))
                      }
                   )

map5

Data Order

What's happening here?

sfhomes_low2high <- sfhomes[order(sfhomes$Value, decreasing = FALSE),]

map4 <- leaflet(sfhomes_low2high) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(~lon, ~lat, popup=popup_content,
             fillColor= ~quantColors(Value),
             radius=6, color="grey", weight=1,fillOpacity = 1
             ) 

Does the output map look different from previous map?

map4

Questions

Recap

Recap

Basic Maps

  • addMarkers - Simple Marker Maps
  • addCircleMarkers - Circle Marker Maps

Data Maps

  • addCircles Proportional symbol maps
  • colorFactor - Category Maps
  • colorNumeric - Proportional color maps
  • colorQuantile - Graduated color maps

Geographic data

We have been working with geographic data in data frames

The coordinates are longitude and latitude

Next up, more complex spatial objects

Spatial Data in R

Spatial Data in R

We can use the sp and `rgdal libaries to import, manipulate and map more complex spatial objects.

sp - R classes and methods for spatial data

rgdal - Functions for importing and transforming spatial data

Let's use these to import data in ESRI Shapefiles

Read in an ESRI Shapefile

Read in the data

sf_md_hhi <- readOGR(dsn="data",layer="sf_medhhincome_acs5y_16")
## OGR data source with driver: ESRI Shapefile 
## Source: "data", layer: "sf_medhhincome_acs5y_16"
## with 196 features
## It has 5 fields

Explore the data

class(sf_md_hhi)
## [1] "SpatialPolygonsDataFrame"
## attr(,"package")
## [1] "sp"
head(sf_md_hhi)
## An object of class "SpatialPolygonsDataFrame"
## Slot "data":
##         GEOID                                                  NAME
## 0 06075010700    Census Tract 107, San Francisco County, California
## 1 06075011200    Census Tract 112, San Francisco County, California
## 2 06075011901 Census Tract 119.01, San Francisco County, California
## 3 06075012201 Census Tract 122.01, San Francisco County, California
## 4 06075013500    Census Tract 135, San Francisco County, California
## 5 06075015200    Census Tract 152, San Francisco County, California
##     variable estimate   moe
## 0 B19013_001    22425  2716
## 1 B19013_001   100139 16777
## 2 B19013_001    72364 17240
## 3 B19013_001    43698 12316
## 4 B19013_001   120714 15421
## 5 B19013_001    89440 18460
## 
## Slot "polygons":
## [[1]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.41005   37.79851
## 
## Slot "area":
## [1] 1.873656e-05
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##            [,1]     [,2]
##  [1,] -122.4129 37.80218
##  [2,] -122.4114 37.80112
##  [3,] -122.4090 37.79949
##  [4,] -122.4079 37.79870
##  [5,] -122.4071 37.79815
##  [6,] -122.4067 37.79787
##  [7,] -122.4055 37.79706
##  [8,] -122.4084 37.79670
##  [9,] -122.4117 37.79629
## [10,] -122.4119 37.79724
## [11,] -122.4123 37.79910
## [12,] -122.4125 37.80003
## [13,] -122.4128 37.80189
## [14,] -122.4129 37.80218
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.41005   37.79851
## 
## Slot "ID":
## [1] "0"
## 
## Slot "area":
## [1] 1.873656e-05
## 
## 
## [[2]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.41273   37.79297
## 
## Slot "area":
## [1] 1.813447e-05
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##            [,1]     [,2]
##  [1,] -122.4163 37.79389
##  [2,] -122.4152 37.79403
##  [3,] -122.4114 37.79453
##  [4,] -122.4097 37.79474
##  [5,] -122.4095 37.79385
##  [6,] -122.4092 37.79204
##  [7,] -122.4125 37.79163
##  [8,] -122.4158 37.79121
##  [9,] -122.4159 37.79213
## [10,] -122.4161 37.79301
## [11,] -122.4163 37.79389
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.41273   37.79297
## 
## Slot "ID":
## [1] "1"
## 
## Slot "area":
## [1] 1.813447e-05
## 
## 
## [[3]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.41391   37.79047
## 
## Slot "area":
## [1] 6.371875e-06
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##           [,1]     [,2]
## [1,] -122.4158 37.79121
## [2,] -122.4125 37.79163
## [3,] -122.4123 37.79067
## [4,] -122.4121 37.78974
## [5,] -122.4154 37.78932
## [6,] -122.4156 37.79025
## [7,] -122.4158 37.79121
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.41391   37.79047
## 
## Slot "ID":
## [1] "2"
## 
## Slot "area":
## [1] 6.371875e-06
## 
## 
## [[4]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.41635   37.78585
## 
## Slot "area":
## [1] 9.413017e-06
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##           [,1]     [,2]
## [1,] -122.4183 37.78704
## [2,] -122.4150 37.78745
## [3,] -122.4146 37.78558
## [4,] -122.4144 37.78466
## [5,] -122.4161 37.78445
## [6,] -122.4177 37.78424
## [7,] -122.4179 37.78517
## [8,] -122.4183 37.78704
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.41635   37.78585
## 
## Slot "ID":
## [1] "3"
## 
## Slot "area":
## [1] 9.413017e-06
## 
## 
## [[5]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.4326   37.7909
## 
## Slot "area":
## [1] 2.405882e-05
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##            [,1]     [,2]
##  [1,] -122.4362 37.79226
##  [2,] -122.4346 37.79247
##  [3,] -122.4296 37.79310
##  [4,] -122.4293 37.79134
##  [5,] -122.4289 37.78953
##  [6,] -122.4338 37.78891
##  [7,] -122.4355 37.78870
##  [8,] -122.4358 37.79050
##  [9,] -122.4362 37.79226
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.4326   37.7909
## 
## Slot "ID":
## [1] "4"
## 
## Slot "area":
## [1] 2.405882e-05
## 
## 
## [[6]]
## An object of class "Polygons"
## Slot "Polygons":
## [[1]]
## An object of class "Polygon"
## Slot "labpt":
## [1] -122.43027   37.78791
## 
## Slot "area":
## [1] 2.856017e-05
## 
## Slot "hole":
## [1] FALSE
## 
## Slot "ringDir":
## [1] 1
## 
## Slot "coords":
##            [,1]     [,2]
##  [1,] -122.4355 37.78870
##  [2,] -122.4338 37.78891
##  [3,] -122.4289 37.78953
##  [4,] -122.4256 37.78995
##  [5,] -122.4250 37.78713
##  [6,] -122.4283 37.78671
##  [7,] -122.4316 37.78629
##  [8,] -122.4349 37.78588
##  [9,] -122.4355 37.78870
## 
## 
## 
## Slot "plotOrder":
## [1] 1
## 
## Slot "labpt":
## [1] -122.43027   37.78791
## 
## Slot "ID":
## [1] "5"
## 
## Slot "area":
## [1] 2.856017e-05
## 
## 
## 
## Slot "plotOrder":
## [1] 6 5 1 2 4 3
## 
## Slot "bbox":
##          min        max
## x -122.43620 -122.40551
## y   37.78424   37.80218
## 
## Slot "proj4string":
## CRS arguments:
##  +proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0

Map an SpatialPolygonsDataFrame

Leaflet understands sp objects!!!

map6 <- leaflet() %>%
  addTiles() %>%
  addPolygons(data=sf_md_hhi)

Map an SpatialPolygonsDataFrame

map6

Adding custom symbology

?addPolygons

addPolygons(map, lng = NULL, lat = NULL, layerId = NULL, group = NULL,
            stroke = TRUE, color = "#03F", weight = 5, opacity = 0.5,
            fill = TRUE, fillColor = color, fillOpacity = 0.2, 
            dashArray = NULL, smoothFactor = 1, noClip = FALSE, 
            popup = NULL, popupOptions = NULL, label = NULL, 
            labelOptions = NULL, options = pathOptions(),
            highlightOptions = NULL, data = getMapData(map))

Adding custom symbology

map6 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(data=sf_md_hhi, color="grey", weight=1,
              fillColor="Orange", fillOpacity = 0.25)

SF Census Tracts

map6

Choropleth Maps

Choropleth Maps

Color regions based on data values.

The data values are classified into bins.

  • Quantile classification is the default.

Each bin gets a unique color from a color palette.

Create a choropleth map

Median Household Income is in the estimate column

Recipe:

  1. Create a color function based on the values of estimate
  2. Map the polygons setting the color to values produced by the color function

Color palette

##display.brewer.all(type="seq")
quantColors <- colorQuantile("YlOrRd", sf_md_hhi$estimate, n=5)

Apply the palette

map6 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColors(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate))

Census Tracts by Med HH Income

map6

Add a legend

map6 <- map6 %>% addLegend(pal = quantColors, 
                   values = sf_md_hhi$estimate,
                   title = "Median HH Income",
                   position="bottomleft",
                   opacity=1,
                   labFormat = function(type, cuts, p) {
                     n = length(cuts)
                     cuts = paste0("$", format(cuts[-n], big.mark=","), 
                             " - ", "$",format(cuts[-1], big.mark=","))
                   }
)

Med HH Income Legend

map6

Map Overlays

You can add multiple data layers to a leaflet map.

Let's add the assessed housing value points and see if we can find some cheaper houses in High Income census tracts.

cheap <- sfhomes[sfhomes$Value < 1000000,]

map7 <- leaflet() %>%
  setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColors(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate)) %>%
  addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75)

Map Overlays

map7

Getting Control

To add 'controls' to our map, we use the function addLayersControl().

We assign a group to each basemap and layer (polygon, marker, line, etc).

Then layer switcher allows us to toggle between different basemaps and turn on/off each of the layers.

?addLayersControl

Assign a group

map8 <- leaflet() %>%
          setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
          addProviderTiles("CartoDB.Positron") %>%
          addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColors(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate),
              group="Median HH Income"
          ) %>%
          addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75,
              group="Property Values"
          ) %>%
          addLayersControl(
            overlayGroups = c("Property Values","Median HH Income"),
            options = layersControlOptions(collapsed = FALSE)
        )

Layer Controls

map8

Add Another Baselayers

map8 <- leaflet() %>%
          setView(lng=-122.448889, lat=37.764645, zoom=12) %>%
          addProviderTiles("CartoDB.Positron", group="Simple") %>%
          addProviderTiles("Esri.WorldStreetMap", group="Streets")  %>%
          addPolygons(data=sf_md_hhi, color="white", weight=1, opacity=0.5,
              fillColor=~quantColors(estimate), fillOpacity = 0.65,
              popup = paste0("$",sf_md_hhi$estimate),
              group="Median HH Income"
          ) %>%
          addCircleMarkers(data=cheap, popup=paste0("$",cheap$Value),
              color="black",weight=1, radius=6, 
              fillColor="white", fillOpacity = 0.75,
              group="Property Values"
          ) %>%
          addLayersControl(
            baseGroups = c("Simple", "Streets"),
            overlayGroups = c("Property Values","Median HH Income"),
            options = layersControlOptions(collapsed = FALSE)
        )

Layer Controls

map8

Questions?

Sharing

Sharing your web map

Interactive Maps in R - check

Web Maps can be shared if online.

Easy way to go is RPubs

RPubs

You can share you map online by publishing it to RPubs.

  • You need to have an RPubs account to make that work.
  1. Enter the code for your map in the console

  2. In the Viewer window, click on the Publish icon.

RPubs

Saving your map

Another way to share your map is to save it to a file. You can then email it, host it on your own web server or host it on github, etc.

#library(htmlwidgets)
saveWidget(map7, file="testmap.html")

Open your file to by double-clicking on it in the Mac Finder or Windows Explorer.

Questions?

Next Steps

Many R packages for Leaflet maps

tmap

  • great for exploratory data analysis of sp objects within R
  • much easier than leaflet for creating Leaflet maps as well as publication ready static map images.

mapview

  • This is a new package similar to tmap. I haven't checked it out but I keep seeing it so I think it is emerging.

Why leaflet

Upcoming Workshops

Thanks

To you!

and to Josh Pepper who did an earlier workshop on which these materials are loosely based.